package com.yxc.memoryfilesystemdemo.utils;

import net.sf.sevenzipjbinding.impl.RandomAccessFileOutStream;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.DataIO;
import org.mapdb.Serializer;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

// RAR 解压相关导入
import net.sf.sevenzipjbinding.*;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;

public class MemoryFileSystem {
    
    private final DB db;
    private final ConcurrentMap<String, FileEntry> fileStore;
    private final ConcurrentMap<String, List<String>> directoryStructure;

    // 文件条目类，包含文件内容和元数据
    public static class FileEntry implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private byte[] content;
        private long lastModified;
        private Map<String, Object> metadata;
        
        public FileEntry(byte[] content) {
            this.content = content;
            this.lastModified = System.currentTimeMillis();
            this.metadata = new HashMap<>();
        }
        
        public byte[] getContent() {
            return content;
        }
        
        public void setContent(byte[] content) {
            this.content = content;
            this.lastModified = System.currentTimeMillis();
        }
        
        public long getLastModified() {
            return lastModified;
        }
        
        public Map<String, Object> getMetadata() {
            return metadata;
        }
    }
    
    public MemoryFileSystem() {
        // 创建内存中的数据库
        this.db = DBMaker
                .memoryDB()
                .make();

        // 创建文件存储
        this.fileStore = db.hashMap("fileStore", Serializer.STRING, new Serializer<FileEntry>() {
            @Override
            public void serialize(DataOutput out, FileEntry value) throws IOException {
                ObjectOutputStream out2 = new ObjectOutputStream((OutputStream) out);
                out2.writeObject(value);
                out2.flush();
            }

            @Override
            public FileEntry deserialize(DataInput in, int available) throws IOException {
                try {
                    ObjectInputStream in2 = new ObjectInputStream(new DataIO.DataInputToStream(in));
                    return (FileEntry) in2.readObject();
                } catch (ClassNotFoundException e) {
                    throw new IOException(e);
                }
            }
        });

        // 创建目录结构
        this.directoryStructure = db.hashMap("directoryStructure", Serializer.STRING, new Serializer<List<String>>() {
            @Override
            public void serialize(DataOutput out, List<String> value) throws IOException {
                ObjectOutputStream out2 = new ObjectOutputStream((OutputStream) out);
                out2.writeObject(value);
                out2.flush();
            }

            @Override
            public List<String> deserialize(DataInput in, int available) throws IOException {
                try {
                    ObjectInputStream in2 = new ObjectInputStream(new DataIO.DataInputToStream(in));
                    return (List<String>) in2.readObject();
                } catch (ClassNotFoundException e) {
                    throw new IOException(e);
                }
            }
        });
        
        // 初始化根目录
        directoryStructure.putIfAbsent("/", new ArrayList<>());
    }

    // 检查路径是否存在
    public boolean exists(String path) {
        String normalizedPath = normalizePath(path);
        return fileStore.containsKey(normalizedPath) || directoryStructure.containsKey(normalizedPath);
    }

    // 判断路径是否为文件
    public boolean isFile(String path) {
        String normalizedPath = normalizePath(path);
        return fileStore.containsKey(normalizedPath);
    }

    // 判断路径是否为目录
    public boolean isDirectory(String path) {
        String normalizedPath = normalizePath(path);
        return directoryStructure.containsKey(normalizedPath);
    }

    // 获取文件大小
    public long getFileSize(String filePath) {
        String normalizedPath = normalizePath(filePath);
        FileEntry entry = fileStore.get(normalizedPath);
        return entry != null ? entry.getContent().length : 0;
    }

    // 获取文件最后修改时间
    public long getLastModified(String path) {
        String normalizedPath = normalizePath(path);
        FileEntry entry = fileStore.get(normalizedPath);
        return entry != null ? entry.getLastModified() : 0;
    }

    // 创建一个空文件
    public void createFile(String filePath) {
        // 使用空字节数组作为文件内容
        writeFile(filePath, new byte[0]);
    }

    // 创建一个带有初始内容的文件
    public void createFile(String filePath, byte[] content) {
        writeFile(filePath, content);
    }

    // 创建一个带有初始文本内容的文件
    public void createFile(String filePath, String content) {
        writeFile(filePath, content.getBytes(StandardCharsets.UTF_8));
    }

    // 创建目录
    public void createDirectory(String directoryPath) {
        // 规范化路径
        String normalizedPath = normalizePath(directoryPath);

        // 检查目录是否已存在
        if (directoryStructure.containsKey(normalizedPath)) {
            return; // 目录已存在，直接返回
        }
        
        // 确保父目录存在
        String parentDir = getParentPath(normalizedPath);
        ensureDirectoryExists(parentDir);
        
        // 添加到目录结构
        directoryStructure.putIfAbsent(normalizedPath, new ArrayList<>());
        
        // 更新父目录
        if (!"/".equals(parentDir)) {
            List<String> children = directoryStructure.get(parentDir);
            String dirName = getFileName(normalizedPath);
            if (!children.contains(dirName)) {
                children.add(dirName);
            }
        }
    }
    
    // 写入文件
    public void writeFile(String filePath, byte[] content) {
        // 规范化路径
        String normalizedPath = normalizePath(filePath);
        
        // 确保目录存在
        String parentDir = getParentPath(normalizedPath);
        ensureDirectoryExists(parentDir);
        
        // 写入文件内容
        fileStore.put(normalizedPath, new FileEntry(content));
        
        // 更新目录结构
        List<String> children = directoryStructure.get(parentDir);
        String fileName = getFileName(normalizedPath);
        if (!children.contains(fileName)) {
            children.add(fileName);
        }
    }

    // 将字符串列表写入文件，每行一个字符串
    public void writeLines(String filePath, List<String> lines, Charset charset) {
        // 检查参数
        if (lines == null) {
            throw new IllegalArgumentException("字符串列表不能为空");
        }

        // 使用换行符连接所有字符串
        StringBuilder contentBuilder = new StringBuilder();
        for (int i = 0; i < lines.size(); i++) {
            contentBuilder.append(lines.get(i));
            // 除了最后一行，每行后面添加换行符
            if (i < lines.size() - 1) {
                contentBuilder.append("\n");
            }
        }

        // 将内容写入文件
        writeFile(filePath, contentBuilder.toString().getBytes(charset));
    }
    
    // 读取文件
    public byte[] readFile(String filePath) {
        String normalizedPath = normalizePath(filePath);
        FileEntry entry = fileStore.get(normalizedPath);
        return entry != null ? entry.getContent() : null;
    }

    // 读取文件内容为字符串列表，每行作为一个元素
    public List<String> readLines(String filePath, Charset charset) {
        String normalizedPath = normalizePath(filePath);

        // 检查文件是否存在
        if (!fileStore.containsKey(normalizedPath)) {
            return new ArrayList<>();
        }

        // 获取文件内容
        byte[] content = fileStore.get(normalizedPath).getContent();

        // 使用UTF-8编码将字节转换为字符串
        String fileContent = new String(content, charset);

        // 按行分割并返回列表
        return Arrays.asList(fileContent.split("\\r?\\n"));
    }


    // 获取文件元数据
    public Map<String, Object> getFileMetadata(String filePath) {
        String normalizedPath = normalizePath(filePath);
        FileEntry entry = fileStore.get(normalizedPath);
        return entry != null ? entry.getMetadata() : null;
    }
    
    // 设置文件元数据
    public void setFileMetadata(String filePath, String key, Object value) {
        String normalizedPath = normalizePath(filePath);
        FileEntry entry = fileStore.get(normalizedPath);
        if (entry != null) {
            entry.getMetadata().put(key, value);
        }
    }
    
    // 获取目录内容
    public List<String> listDirectory(String directoryPath) {
        String normalizedPath = normalizePath(directoryPath);
        List<String> children = directoryStructure.get(normalizedPath);
        return children != null ? new ArrayList<>(children) : Collections.emptyList();
    }
    
    // 删除文件或目录
    public void delete(String path) {
        String normalizedPath = normalizePath(path);
        
        if (fileStore.containsKey(normalizedPath)) {
            // 删除文件
            fileStore.remove(normalizedPath);
            
            // 更新父目录
            String parentDir = getParentPath(normalizedPath);
            List<String> children = directoryStructure.get(parentDir);
            if (children != null) {
                children.remove(getFileName(normalizedPath));
            }
        } else if (directoryStructure.containsKey(normalizedPath)) {
            // 删除目录及其内容
            deleteDirectoryRecursively(normalizedPath);
        }
    }
    
    // 递归删除目录
    private void deleteDirectoryRecursively(String directoryPath) {
        // 删除目录中的所有文件
        List<String> children = directoryStructure.get(directoryPath);
        if (children != null) {
            for (String child : new ArrayList<>(children)) {
                String childPath = directoryPath.equals("/") 
                        ? "/" + child 
                        : directoryPath + "/" + child;
                
                if (fileStore.containsKey(childPath)) {
                    // 删除文件
                    fileStore.remove(childPath);
                } else if (directoryStructure.containsKey(childPath)) {
                    // 递归删除子目录
                    deleteDirectoryRecursively(childPath);
                }
            }
        }
        
        // 删除目录自身
        directoryStructure.remove(directoryPath);
        
        // 更新父目录
        String parentDir = getParentPath(directoryPath);
        if (!"/".equals(parentDir) && directoryStructure.containsKey(parentDir)) {
            List<String> parentChildren = directoryStructure.get(parentDir);
            parentChildren.remove(getFileName(directoryPath));
        }
    }
    
    // 确保目录存在
    private void ensureDirectoryExists(String directoryPath) {
        if (!directoryStructure.containsKey(directoryPath)) {
            createDirectory(directoryPath);
        }
    }
    
    // 规范化路径
    public String normalizePath(String path) {
        if (path == null || path.isEmpty()) {
            return "/";
        }
        
        // 移除多余的斜杠并确保以斜杠开头
        Path normalized = Paths.get(path).normalize();
        String result = normalized.toString().replace("\\", "/");
        result = result.replace(":", "");
        if (!result.startsWith("/")) {
            result = "/" + result;
        }
        
        return result;
    }
    
    // 获取父路径
    private String getParentPath(String path) {
        if ("/".equals(path)) {
            return "/";
        }
        
        int lastSlash = path.lastIndexOf('/');
        return lastSlash > 0 ? path.substring(0, lastSlash) : "/";
    }
    
    // 获取文件名
    private String getFileName(String path) {
        if ("/".equals(path)) {
            return "/";
        }
        
        int lastSlash = path.lastIndexOf('/');
        return lastSlash >= 0 ? path.substring(lastSlash + 1) : path;
    }
    
    // 关闭数据库，释放资源
    public void close() {
        db.close();
    }
    
    // 递归遍历文件系统，获取所有文件的路径
    public List<String> loopFiles(String directoryPath) {
        List<String> result = new ArrayList<>();
        String normalizedPath = normalizePath(directoryPath);
        
        // 检查目录是否存在
        if (!directoryStructure.containsKey(normalizedPath)) {
            return result;
        }
        
        // 获取目录中的所有子项
        List<String> children = directoryStructure.get(normalizedPath);
        if (children == null || children.isEmpty()) {
            return result;
        }
        
        // 遍历所有子项
        for (String child : children) {
            String childPath = normalizedPath.equals("/") 
                    ? "/" + child 
                    : normalizedPath + "/" + child;
            
            // 如果是文件，添加到结果列表
            if (fileStore.containsKey(childPath)) {
                result.add(childPath);
            } 
            // 如果是目录，递归遍历
            else if (directoryStructure.containsKey(childPath)) {
                result.addAll(loopFiles(childPath));
            }
        }
        
        return result;
    }

    // 解压ZIP文件到指定路径
    public List<String> unzipFile(byte[] zipData, String targetPath) throws IOException {
        List<String> extractedFiles = new ArrayList<>();
        String normalizedTargetPath = normalizePath(targetPath);

        // 确保目标路径是一个目录
        ensureDirectoryExists(normalizedTargetPath);

        try (ByteArrayInputStream bais = new ByteArrayInputStream(zipData);
             ZipInputStream zis = new ZipInputStream(bais)) {

            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                String entryPath = normalizePath(normalizedTargetPath + "/" + entry.getName());

                if (entry.isDirectory()) {
                    // 如果是目录，创建目录
                    createDirectory(entryPath);
                } else {
                    // 如果是文件，读取内容并写入
                    byte[] content = new byte[zis.available()];
                    int bytesRead;
                    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                        while ((bytesRead = zis.read(content)) != -1) {
                            baos.write(content, 0, bytesRead);
                        }
                        content = baos.toByteArray();
                    }

                    writeFile(entryPath, content);
                    extractedFiles.add(entryPath);
                }

                zis.closeEntry();
            }
        }

        return extractedFiles;
    }

    // 解压ZIP文件到指定路径，若解压后只有一个根目录，则去除这一层目录
    public List<String> unzipFile(byte[] zipData, String targetPath, boolean flattenSingleDirectory) throws IOException {
        List<String> extractedFiles = new ArrayList<>();
        String normalizedTargetPath = normalizePath(targetPath);

        // 确保目标路径是一个目录
        ensureDirectoryExists(normalizedTargetPath);

        // 用于存储解压后的顶级条目
        Set<String> topLevelEntries = new HashSet<>();

        try (ByteArrayInputStream bais = new ByteArrayInputStream(zipData);
             ZipInputStream zis = new ZipInputStream(bais)) {

            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                String entryPath = entry.getName();

                // 提取顶级条目名称（第一个斜杠前的部分）
                int firstSlash = entryPath.indexOf('/');
                if (firstSlash > 0) {
                    String topLevelName = entryPath.substring(0, firstSlash);
                    topLevelEntries.add(topLevelName);
                } else {
                    // 如果没有斜杠，说明是顶级文件
                    topLevelEntries.add(entryPath);
                }

                zis.closeEntry();
            }
        }

        // 重置输入流以便再次读取
        try (ByteArrayInputStream bais = new ByteArrayInputStream(zipData);
             ZipInputStream zis = new ZipInputStream(bais)) {

            // 检查是否只有一个顶级目录
            boolean hasSingleTopLevelDir = topLevelEntries.size() == 1;
            String singleDirName = null;

            if (hasSingleTopLevelDir) {
                for (String name : topLevelEntries) {
                    // 检查这个顶级条目是否是目录
                    try (ByteArrayInputStream bais2 = new ByteArrayInputStream(zipData);
                         ZipInputStream zis2 = new ZipInputStream(bais2)) {

                        ZipEntry entry;
                        boolean isDirectory = false;

                        while ((entry = zis2.getNextEntry()) != null) {
                            String entryPath = entry.getName();

                            // 如果条目路径以这个顶级名称开头，并且长度更长，说明它是一个目录
                            if (entryPath.startsWith(name + "/") && entryPath.length() > name.length() + 1) {
                                isDirectory = true;
                                singleDirName = name;
                                break;
                            }

                            zis2.closeEntry();
                        }

                        hasSingleTopLevelDir = isDirectory;
                    }
                }
            }

            // 重新读取ZIP文件并解压
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                String entryPath = entry.getName();
                String finalPath;

                if (hasSingleTopLevelDir && flattenSingleDirectory && entryPath.startsWith(singleDirName + "/")) {
                    // 去除单层目录
                    finalPath = entryPath.substring(singleDirName.length() + 1);
                } else {
                    finalPath = entryPath;
                }

                String normalizedFinalPath = normalizePath(normalizedTargetPath + "/" + finalPath);

                if (entry.isDirectory()) {
                    // 如果是目录，创建目录
                    createDirectory(normalizedFinalPath);
                } else {
                    // 如果是文件，读取内容并写入
                    byte[] content = new byte[zis.available()];
                    int bytesRead;
                    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                        while ((bytesRead = zis.read(content)) != -1) {
                            baos.write(content, 0, bytesRead);
                        }
                        content = baos.toByteArray();
                    }

                    writeFile(normalizedFinalPath, content);
                    extractedFiles.add(normalizedFinalPath);
                }

                zis.closeEntry();
            }
        }

        return extractedFiles;
    }
    
    // 解压RAR文件到指定路径
    public List<String> unrarFile(byte[] rarData, String targetPath) throws IOException {
        List<String> extractedFiles = new ArrayList<>();
        String normalizedTargetPath = normalizePath(targetPath);

        // 确保目标路径是一个目录
        ensureDirectoryExists(normalizedTargetPath);

        // 创建临时文件来存储 RAR 数据
        File tempFile = File.createTempFile("rar_", ".rar");
        try {
            // 写入 RAR 数据到临时文件
            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                fos.write(rarData);
            }

            // 使用 7-Zip-JBinding 解压
            try (RandomAccessFileInStream randomAccessFile = new RandomAccessFileInStream(new RandomAccessFile(tempFile, "r"))) {
                IInArchive archive = SevenZip.openInArchive(null, randomAccessFile);
                
                try {
                    ISimpleInArchive simpleInArchive = archive.getSimpleInterface();
                    
                    for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                        String path = item.getPath();
                        
                        if (item.isFolder()) {
                            // 如果是目录，创建目录
                            String dirPath = normalizePath(normalizedTargetPath + "/" + path);
                            createDirectory(dirPath);
                        } else {
                            // 如果是文件，读取内容并写入
                            String filePath = normalizePath(normalizedTargetPath + "/" + path);
                            
                            // 确保父目录存在
                            String parentDir = getParentPath(filePath);
                            ensureDirectoryExists(parentDir);
                            
                            // 读取文件内容
                            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                                item.extractSlow(new ISequentialOutStream() {
                                    @Override
                                    public int write(byte[] data) throws SevenZipException {
                                        try {
                                            baos.write(data);
                                            return data.length;
                                        } catch (IOException e) {
                                            throw new SevenZipException(e);
                                        }
                                    }
                                });
                                byte[] content = baos.toByteArray();
                                
                                writeFile(filePath, content);
                                extractedFiles.add(filePath);
                            }
                        }
                    }
                } finally {
                    archive.close();
                }
            }
        } finally {
            // 删除临时文件
            tempFile.delete();
        }

        return extractedFiles;
    }
    
    // 解压RAR文件到指定路径，若解压后只有一个根目录，则去除这一层目录
    public List<String> unrarFile(byte[] rarData, String targetPath, boolean flattenSingleDirectory) throws IOException {
        List<String> extractedFiles = new ArrayList<>();
        String normalizedTargetPath = normalizePath(targetPath);

        // 确保目标路径是一个目录
        ensureDirectoryExists(normalizedTargetPath);

        // 创建临时文件来存储 RAR 数据
        File tempFile = File.createTempFile("rar_", ".rar");
        try {
            // 写入 RAR 数据到临时文件
            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                fos.write(rarData);
            }

            // 用于存储解压后的顶级条目
            Set<String> topLevelEntries = new HashSet<>();
            String singleDirName = null;

            // 第一次遍历，收集顶级条目
            try (RandomAccessFileInStream randomAccessFile = new RandomAccessFileInStream(new RandomAccessFile(tempFile, "r"))) {
                IInArchive archive = SevenZip.openInArchive(null, randomAccessFile);
                
                try {
                    ISimpleInArchive simpleInArchive = archive.getSimpleInterface();
                    
                    for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                        String entryPath = item.getPath();

                        // 提取顶级条目名称（第一个斜杠前的部分）
                        int firstSlash = entryPath.indexOf('/');
                        if (firstSlash > 0) {
                            String topLevelName = entryPath.substring(0, firstSlash);
                            topLevelEntries.add(topLevelName);
                        } else {
                            // 如果没有斜杠，说明是顶级文件
                            topLevelEntries.add(entryPath);
                        }
                    }
                } finally {
                    archive.close();
                }
            }

            // 检查是否只有一个顶级目录
            boolean hasSingleTopLevelDir = topLevelEntries.size() == 1;

            if (hasSingleTopLevelDir) {
                String topLevelName = topLevelEntries.iterator().next();
                try (RandomAccessFileInStream randomAccessFile = new RandomAccessFileInStream(new RandomAccessFile(tempFile, "r"))) {
                    IInArchive archive = SevenZip.openInArchive(null, randomAccessFile);
                    
                    try {
                        ISimpleInArchive simpleInArchive = archive.getSimpleInterface();
                        boolean isDirectory = false;

                        for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                            String entryPath = item.getPath();

                            // 如果条目路径以这个顶级名称开头，并且长度更长，说明它是一个目录
                            if (entryPath.startsWith(topLevelName + "/") && entryPath.length() > topLevelName.length() + 1) {
                                isDirectory = true;
                                singleDirName = topLevelName;
                                break;
                            }
                        }

                        hasSingleTopLevelDir = isDirectory;
                    } finally {
                        archive.close();
                    }
                }
            }

            // 重新读取RAR文件并解压
            try (RandomAccessFileInStream randomAccessFile = new RandomAccessFileInStream(new RandomAccessFile(tempFile, "r"))) {
                IInArchive archive = SevenZip.openInArchive(null, randomAccessFile);
                
                try {
                    ISimpleInArchive simpleInArchive = archive.getSimpleInterface();
                    
                    for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                        String entryPath = item.getPath();
                        String finalPath;

                        if (hasSingleTopLevelDir && flattenSingleDirectory && singleDirName != null && entryPath.startsWith(singleDirName + "/")) {
                            // 去除单层目录
                            finalPath = entryPath.substring(singleDirName.length() + 1);
                        } else {
                            finalPath = entryPath;
                        }

                        String normalizedFinalPath = normalizePath(normalizedTargetPath + "/" + finalPath);

                        if (item.isFolder()) {
                            // 如果是目录，创建目录
                            createDirectory(normalizedFinalPath);
                        } else {
                            // 如果是文件，读取内容并写入
                            // 确保父目录存在
                            String parentDir = getParentPath(normalizedFinalPath);
                            ensureDirectoryExists(parentDir);
                            
                            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                                item.extractSlow(new ISequentialOutStream() {
                                    @Override
                                    public int write(byte[] data) throws SevenZipException {
                                        try {
                                            baos.write(data);
                                            return data.length;
                                        } catch (IOException e) {
                                            throw new SevenZipException(e);
                                        }
                                    }
                                });
                                byte[] content = baos.toByteArray();
                                
                                writeFile(normalizedFinalPath, content);
                                extractedFiles.add(normalizedFinalPath);
                            }
                        }
                    }
                } finally {
                    archive.close();
                }
            }
        } finally {
            // 删除临时文件
            tempFile.delete();
        }

        return extractedFiles;
    }
    
    // 示例用法
    public static void main(String[] args) {
        String a = "/C:/data/exploit_patch/tmp/znc-znc-1.8.2.zip";
        System.out.println(a.replace(":", ""));
        
        // RAR 解压示例
        MemoryFileSystem fs = new MemoryFileSystem();
        try {
            // 创建测试目录
            fs.createDirectory("/rar_test");
            fs.writeFile("/rar_test/hello.txt", "Hello, RAR!".getBytes(StandardCharsets.UTF_8));
            
            System.out.println("基本文件操作测试完成");
            System.out.println("RAR 解压功能已修复，支持 RAR 4.x 和 RAR 5.x 格式");
            System.out.println("使用方法:");
            System.out.println("1. 基本解压: fs.unrarFile(rarData, \"/target_path\")");
            System.out.println("2. 去除单层目录: fs.unrarFile(rarData, \"/target_path\", true)");
            
        } catch (Exception e) {
            System.err.println("测试过程中出现错误: " + e.getMessage());
        } finally {
            fs.close();
        }
        
        // MemoryFileSystem fs = new MemoryFileSystem();
    }

    /**
     * 获取某个目录下所有可作为文本文件读取的文件的总行数
     * @param directoryPath 目录路径
     * @return 总行数
     */
    public int countTextFileLines(String directoryPath) {
        List<String> allFiles = loopFiles(directoryPath);
        int totalLines = 0;
        for (String file : allFiles) {
            try {
                List<String> lines = readLines(file, StandardCharsets.UTF_8);
                totalLines += lines.size();
            } catch (Exception e) {
                // 非文本文件或解码失败，忽略
            }
        }
        return totalLines;
    }

    /**
     * 获取某个目录下所有可作为文本文件读取的文件的数量
     * @param directoryPath 目录路径
     * @return 文本文件数量
     */
    public int countTextFiles(String directoryPath) {
        List<String> allFiles = loopFiles(directoryPath);
        int fileCount = 0;
        for (String file : allFiles) {
            try {
                readLines(file, StandardCharsets.UTF_8);
                fileCount++;
            } catch (Exception e) {
                // 非文本文件或解码失败，忽略
            }
        }
        return fileCount;
    }

    /**
     * 探测某个文件夹下的主要源代码语言（出现文件数最多的语言）
     * 通过文件后缀判断，支持常见主流语言
     * @param directoryPath 目录路径
     * @return 主要语言（如"Java"），没有则返回null
     */
    public String detectMainLanguageInDirectory(String directoryPath) {
        Map<String, String> extToLang = new HashMap<>();
        extToLang.put(".java", "Java");
        extToLang.put(".py", "Python");
        extToLang.put(".c", "C");
        extToLang.put(".cpp", "C++");
        extToLang.put(".cc", "C++");
        extToLang.put(".h", "C/C++ Header");
        extToLang.put(".hpp", "C++ Header");
        extToLang.put(".js", "JavaScript");
        extToLang.put(".ts", "TypeScript");
        extToLang.put(".go", "Go");
        extToLang.put(".kt", "Kotlin");
        extToLang.put(".kts", "Kotlin Script");
        extToLang.put(".scala", "Scala");
        extToLang.put(".rb", "Ruby");
        extToLang.put(".php", "PHP");
        extToLang.put(".cs", "C#");
        extToLang.put(".sh", "Shell");
        extToLang.put(".bat", "Batch");
        extToLang.put(".sql", "SQL");
        extToLang.put(".html", "HTML");
        extToLang.put(".htm", "HTML");
        extToLang.put(".css", "CSS");
        extToLang.put(".xml", "XML");
        extToLang.put(".json", "JSON");
        extToLang.put(".yml", "YAML");
        extToLang.put(".yaml", "YAML");
        extToLang.put(".md", "Markdown");
        extToLang.put(".swift", "Swift");
        extToLang.put(".rs", "Rust");
        extToLang.put(".dart", "Dart");
        extToLang.put(".m", "Objective-C");
        extToLang.put(".pl", "Perl");
        extToLang.put(".lua", "Lua");
        extToLang.put(".groovy", "Groovy");
        extToLang.put(".r", "R");
        extToLang.put(".erl", "Erlang");
        extToLang.put(".ex", "Elixir");
        extToLang.put(".exs", "Elixir");
        extToLang.put(".dart", "Dart");
        extToLang.put(".scala", "Scala");
        extToLang.put(".vue", "Vue");
        extToLang.put(".jsx", "JavaScript (React)");
        extToLang.put(".tsx", "TypeScript (React)");

        Map<String, Integer> langCount = new HashMap<>();
        List<String> allFiles = loopFiles(directoryPath);
        for (String file : allFiles) {
            int idx = file.lastIndexOf('.');
            if (idx != -1) {
                String ext = file.substring(idx).toLowerCase();
                String lang = extToLang.get(ext);
                if (lang != null) {
                    langCount.put(lang, langCount.getOrDefault(lang, 0) + 1);
                }
            }
        }
        String mainLang = null;
        int maxCount = 0;
        for (Map.Entry<String, Integer> entry : langCount.entrySet()) {
            if (entry.getValue() > maxCount) {
                mainLang = entry.getKey();
                maxCount = entry.getValue();
            }
        }
        return mainLang;
    }

    /**
     * 基于文件内容简单探测某个文件夹下的主要源代码语言（出现文件数最多的语言）
     * 只读取每个文件前几行，基于常见关键字和结构判断
     * @param directoryPath 目录路径
     * @return 主要语言（如"Java"），没有则返回null
     */
    public String detectMainLanguageByContent(String directoryPath) {
        List<String> allFiles = loopFiles(directoryPath);
        Map<String, Integer> langCount = new HashMap<>();
        for (String file : allFiles) {
            List<String> lines;
            try {
                lines = readLines(file, StandardCharsets.UTF_8);
            } catch (Exception e) {
                continue; // 非文本文件
            }
            String lang = detectLanguageFromContent(lines);
            if (lang != null) {
                langCount.put(lang, langCount.getOrDefault(lang, 0) + 1);
            }
        }
        String mainLang = null;
        int maxCount = 0;
        for (Map.Entry<String, Integer> entry : langCount.entrySet()) {
            if (entry.getValue() > maxCount) {
                mainLang = entry.getKey();
                maxCount = entry.getValue();
            }
        }
        return mainLang;
    }

    /**
     * 根据文件内容的前几行简单判断语言类型
     * @param lines 文件内容前几行
     * @return 语言名，如"Java"，无法判断返回null
     */
    private String detectLanguageFromContent(List<String> lines) {
        int maxLines = Math.min(10, lines.size());
        for (int i = 0; i < maxLines; i++) {
            String line = lines.get(i).trim();
            // Java
            if (line.startsWith("package ") || line.startsWith("import ") || line.contains("public class") || line.contains("public interface")) return "Java";
            // Python
            if (line.startsWith("def ") || line.startsWith("import ") || line.startsWith("#") || line.contains("print(") || line.contains("if __name__ == '__main__'")) return "Python";
            // C/C++
            if (line.startsWith("#include") || line.contains("int main(") || line.contains("std::")) return "C/C++";
            // JavaScript
            if (line.startsWith("function ") || line.contains("console.log") || line.contains("var ") || line.contains("let ") || line.contains("const ")) return "JavaScript";
            // TypeScript
            if (line.contains(": string") || line.contains(": number") || line.contains("export class") || line.contains("import {")) return "TypeScript";
            // Shell
            if (line.startsWith("#!/bin/bash") || line.startsWith("#!/usr/bin/env bash") || line.startsWith("echo ")) return "Shell";
            // PHP
            if (line.startsWith("<?php")) return "PHP";
            // C#
            if (line.contains("using System") || line.contains("namespace ") || line.contains("public static void Main")) return "C#";
            // Go
            if (line.startsWith("package main") || line.startsWith("import ") && line.contains("\"") || line.contains("func main()")) return "Go";
            // HTML
            if (line.startsWith("<!DOCTYPE html") || line.startsWith("<html") || line.startsWith("<head") || line.startsWith("<body")) return "HTML";
            // XML
            if (line.startsWith("<?xml")) return "XML";
            // JSON
            if (line.startsWith("{") && line.endsWith("}")) return "JSON";
            // YAML
            if (line.contains(": ") && !line.contains(";") && !line.contains("{") && !line.contains("}")) return "YAML";
            // Markdown
            if (line.startsWith("# ") || line.startsWith("## ") || line.startsWith("- ") || line.startsWith("* ")) return "Markdown";
            // SQL
            if (line.toLowerCase().startsWith("select ") || line.toLowerCase().startsWith("insert ") || line.toLowerCase().startsWith("update ") || line.toLowerCase().startsWith("delete ")) return "SQL";
        }
        return null;
    }
}

